package org.lora.quartz.impl;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import javax.sql.DataSource;

import org.lora.core.configxml.entity.ConfigXmlEntity;
import org.lora.core.context.WebContext;
import org.lora.exception.LoraCoreException;
import org.lora.quartz.IQuartzService;
import org.lora.quartz.SchedulerConstants;
import org.lora.quartz.Test;
import org.lora.quartz.entity.SchedulerInfo;
import org.lora.util.DateUtil;
import org.lora.util.StringUtil;
import org.lora.util.ValidateUtil;
import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.Job;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.SchedulerMetaData;
import org.quartz.SimpleScheduleBuilder;
import org.quartz.SimpleTrigger;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.quartz.impl.StdSchedulerFactory;

/**
 * <p>
 * 类名:QuartzServiceImpl
 * </p>
 * 描述:Quartz服务实现<br>
 * 创建时间:2016年11月8日 下午4:03:01<br>
 * 
 * @author Bladnir@outlook.com<br>
 *         修改历史:<br>
 *         2016年11月8日 Bladnir 创建<br>
 */
public class QuartzServiceImpl implements IQuartzService {

	public static final String DEFAULT_PROPERTIE_FILE_NAME = "quartz.properties";

	private Scheduler scheduler;

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.lora.quartz.IQuartzService#getScheduler()
	 */
	@Override
	public Scheduler getScheduler() {
		return scheduler;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.lora.quartz.IQuartzService#initialize()
	 */
	@Override
	public void initialize() throws SchedulerException {

		DataSource dataSource;
		try {
			dataSource = getDataSource();
			ValidateUtil.isNull(dataSource, "QuartzServiceImpl DataSource");
		} catch (LoraCoreException e1) {
			throw new SchedulerException(e1.getMessage(), e1);
		}

		Properties prop = null;
		try {
			prop = getProperties();
		} catch (Exception e) {
			throw new SchedulerException(e.getMessage(), e);
		}

		try {
			ValidateUtil.isNull(prop, "Quartz Properties Fail");
		} catch (LoraCoreException e) {
			throw new SchedulerException(e.getMessage(), e);
		}

		SchedulerFactory sf = new StdSchedulerFactory(prop, dataSource, "DataSource");
		scheduler = sf.getScheduler();
		scheduler.start();

	}

	/**
	 * 获取quartz需要的数据源（连接池化数据源）
	 * 
	 * @return
	 * @throws LoraCoreException
	 */
	private DataSource getDataSource() throws LoraCoreException {
		WebContext webContext = WebContext.getInstance();
		ConfigXmlEntity configXmlEntity = webContext.getConfigXmlEntity();
		Properties properties = configXmlEntity.getQuartzProperties();

		String quartzDataSource = properties.get("quartzDataSource").toString();
		ValidateUtil.isNull(quartzDataSource, "quartzDataSource");

		Map<String, DataSource> dataSourceMap = webContext.getDataSourceMap();
		ValidateUtil.isNull(dataSourceMap, "dataSourceMap");

		return dataSourceMap.get(quartzDataSource);

	}

	/**
	 * 获取quartz配置文件
	 * 
	 * @return
	 * @throws URISyntaxException
	 * @throws IOException
	 */
	private Properties getProperties() throws URISyntaxException, IOException {
		Properties prop = new Properties();

		String path = Test.class.getClassLoader().getResource(StringUtil.BLANK).toURI().getPath();
		FileInputStream fis = new FileInputStream(new File(path + DEFAULT_PROPERTIE_FILE_NAME));
		prop.load(fis);

		return prop;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.lora.quartz.IQuartzService#getSchedulerState()
	 */
	@Override
	public SchedulerInfo getSchedulerState() throws SchedulerException {

		SchedulerInfo dto = new SchedulerInfo();

		SchedulerMetaData schedulerMetaData = scheduler.getMetaData();

		dto.setNumJobsExecuted(String.valueOf(schedulerMetaData.getNumberOfJobsExecuted()));
		if (schedulerMetaData.isJobStoreSupportsPersistence()) {
			dto.setPersistenceType(SchedulerConstants.PERSISTENCETYPE_DATABASE);
		} else {
			dto.setPersistenceType(SchedulerConstants.PERSISTENCETYPE_MEMORY);
		}
		dto.setRunningSince(DateUtil.getFormatDateLong(DateUtil.YYYY_MM_DD_HH_MM_SS, scheduler.getMetaData().getRunningSince().getTime()));

		dto.setSchedulerName(scheduler.getSchedulerName());
		if (scheduler.isShutdown()) {
			dto.setState(SchedulerConstants.STATUS_STOP);
		} else if (scheduler.isInStandbyMode()) {
			dto.setState(SchedulerConstants.STATUS_PAUSE);
		} else {
			dto.setState(SchedulerConstants.STATUS_START);
		}
		dto.setSummary(schedulerMetaData.getSummary());
		dto.setThreadPoolSize(String.valueOf(schedulerMetaData.getThreadPoolSize()));
		dto.setVersion(schedulerMetaData.getVersion());

		return dto;

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.lora.quartz.IQuartzService#start()
	 */
	@Override
	public void start() throws SchedulerException {
		if (scheduler.isInStandbyMode()) {
			scheduler.start();
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.lora.quartz.IQuartzService#shutdown()
	 */
	@Override
	public void shutdown() throws SchedulerException {
		if (scheduler.isStarted()) {
			scheduler.standby();
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.lora.quartz.IQuartzService#pauseAll()
	 */
	@Override
	public void pauseAll() throws SchedulerException {
		scheduler.pauseAll();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.lora.quartz.IQuartzService#resumeAll()
	 */
	@Override
	public void resumeAll() throws SchedulerException {
		scheduler.resumeAll();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.lora.quartz.IQuartzService#bulidCronTrigger(java.lang.String,
	 * java.lang.String, java.lang.String)
	 */
	@Override
	public CronTrigger bulidCronTrigger(String triggerName, String triggerGroup, String cronExpression) throws SchedulerException {

		CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(triggerName, triggerGroup)
				.withSchedule(CronScheduleBuilder.cronSchedule(cronExpression)).build();
		return trigger;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.lora.quartz.IQuartzService#bulidSimpleTrigger(java.lang.String,
	 * java.lang.String, java.util.Date, java.util.Date, int)
	 */
	@Override
	public SimpleTrigger bulidSimpleTrigger(String name, String group, Date startTime, Date stopTime, int repeatInterval)
			throws SchedulerException {
		SimpleTrigger trigger = TriggerBuilder
				.newTrigger()
				.withIdentity(name, group)
				.startAt(startTime)
				.endAt(stopTime)
				.withSchedule(
						SimpleScheduleBuilder.simpleSchedule().withIntervalInMilliseconds(repeatInterval * 1000)
								.withRepeatCount(SimpleTrigger.REPEAT_INDEFINITELY)).build();
		return trigger;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.lora.quartz.IQuartzService#bulidJob(java.lang.String,
	 * java.lang.String, java.lang.Class)
	 */
	@Override
	public JobDetail bulidJob(String jobName, String jobGroup, Class<? extends Job> jobClass) throws SchedulerException {
		JobDetail job = JobBuilder.newJob(jobClass).withIdentity(jobName, jobGroup).build();
		return job;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.lora.quartz.IQuartzService#blindJobAndTrigger(org.quartz.JobDetail,
	 * org.quartz.Trigger, boolean)
	 */
	@Override
	public void blindJobAndTrigger(JobDetail jobDetail, Trigger trigger, boolean replace) throws SchedulerException {
		// 清除对应缓存内容
		if (checkTriggerExists(trigger.getKey().getName(), trigger.getKey().getGroup())) {
			removeTrigger(trigger.getKey().getName(), trigger.getKey().getGroup());
		}
		Map<JobDetail, List<Trigger>> triggersAndJobs = new HashMap<>();
		List<Trigger> triggers = new ArrayList<>();
		triggers.add(trigger);
		triggersAndJobs.put(jobDetail, triggers);
		blindJobAndTrigger(triggersAndJobs, replace);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.lora.quartz.IQuartzService#blindJobAndTrigger(java.util.Map,
	 * boolean)
	 */
	@Override
	public void blindJobAndTrigger(Map<JobDetail, List<Trigger>> triggersAndJobs, boolean replace) throws SchedulerException {
		scheduler.scheduleJobs(triggersAndJobs, replace);
	}

	public boolean checkTriggerExists(String triggerName, String triggerGroup) throws SchedulerException {
		TriggerKey triggerKey = new TriggerKey(triggerName, triggerGroup);
		return scheduler.checkExists(triggerKey);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.lora.quartz.IQuartzService#executeNow(java.lang.String,
	 * java.lang.String)
	 */
	@Override
	public void executeNow(String jobName, String jobGroup) throws SchedulerException {
		JobKey jobKey = new JobKey(jobName, jobGroup);
		scheduler.triggerJob(jobKey);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.lora.quartz.IQuartzService#pauseTrigger(java.lang.String,
	 * java.lang.String)
	 */
	@Override
	public void pauseTrigger(String triggerName, String group) throws SchedulerException {
		scheduler.pauseTrigger(new TriggerKey(triggerName, group));
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.lora.quartz.IQuartzService#resumeTrigger(java.lang.String,
	 * java.lang.String)
	 */
	@Override
	public void resumeTrigger(String triggerName, String group) throws SchedulerException {
		scheduler.resumeTrigger(new TriggerKey(triggerName, group));
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.lora.quartz.IQuartzService#removeTrigger(java.lang.String,
	 * java.lang.String)
	 */
	@Override
	public void removeTrigger(String triggerName, String triggerGroup) throws SchedulerException {
		TriggerKey triggerKey = new TriggerKey(triggerName, triggerGroup);
		scheduler.unscheduleJob(triggerKey);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.lora.quartz.IQuartzService#getTriggerState(java.lang.String,
	 * java.lang.String)
	 */
	@Override
	public String getTriggerState(String triggerName, String triggerGroup) throws SchedulerException {
		TriggerKey triggerKey = new TriggerKey(triggerName, triggerGroup);
		return scheduler.getTriggerState(triggerKey).toString();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.lora.quartz.IQuartzService#getQrtzTrigger(java.lang.String,
	 * java.lang.String)
	 */
	@Override
	public Trigger getQrtzTrigger(String triggerName, String triggerGroup) throws SchedulerException {
		TriggerKey triggerKey = new TriggerKey(triggerName, triggerGroup);
		return scheduler.getTrigger(triggerKey);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.lora.quartz.IQuartzService#getQrtzJob(java.lang.String,
	 * java.lang.String)
	 */
	@Override
	public JobDetail getQrtzJob(String jobName, String jobGroup) throws SchedulerException {
		JobKey jobKey = new JobKey(jobName, jobGroup);
		return scheduler.getJobDetail(jobKey);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.lora.quartz.IQuartzService#deleteJob(java.lang.String,
	 * java.lang.String)
	 */
	@Override
	public void deleteJob(String jobName, String jobGroup) throws SchedulerException {
		JobKey jobKey = new JobKey(jobName, jobGroup);
		scheduler.deleteJob(jobKey);
	}

	/**
	 * @param jobName
	 * @param jobGroup
	 * @return
	 * @throws SchedulerException
	 */
	@Override
	public List<Trigger> getTriggersFromJob(String jobName, String jobGroup) throws SchedulerException {

		JobKey jobKey = new JobKey(jobName, jobGroup);

		@SuppressWarnings("unchecked")
		List<Trigger> triggers = (List<Trigger>) scheduler.getTriggersOfJob(jobKey);

		return triggers;
	}

}
